/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util.actions;
import java.beans.PropertyChangeEvent;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
/** An action
* dependent on the cookies of the selected nodes.
*
* @author Petr Hamernik, Jaroslav Tulach, Dafe Simonek
*/
public abstract class CookieAction extends NodeAction {
/** name of property with cookies for this action */
private static final String PROP_COOKIES = "cookies"; // NOI18N
/** name of property holding cookies changes listener for this action */
private static final String PROP_COOKIE_CHANGE = "cookieChange"; // NOI18N
/** Action will be enabled if there are one or more selected nodes
* and there is exactly one node which supports the given cookies. */
public static final int MODE_ONE = 0x01;
/** Action will be enabled if there are several selected nodes
* and some of them (at least one, but not all)
* support the given cookies. */
public static final int MODE_SOME = 0x02;
/** Action will be enabled if there are one or more selected nodes
* and all of them support the given cookies. */
public static final int MODE_ALL = 0x04;
/** Action will be enabled if there is exactly one selected node
* and it supports the given cookies. */
public static final int MODE_EXACTLY_ONE = 0x08;
/** Action will be enabled if there are one or more selected nodes
* and any of them (one, all, or some) support the given cookies. */
public static final int MODE_ANY = 0x07;
// [PENDING] 0x06 should suffice, yes? --jglick
static final long serialVersionUID =6031319415908298424L;
/** Get the mode of the action, i.e.<!-- --> how strict it should be about
* cookie support.
* @return the mode of the action. Possible values are disjunctions of the <code>MODE_XXX</code>
* constants. */
protected abstract int mode();
/** Get the cookies that this action requires. The cookies are disjunctive, i.e. a node
* must support AT LEAST ONE of the cookies specified by this method.
*
* @return a list of cookies
*/
protected abstract Class[] cookieClasses ();
/* Initialize the action (and cookies change listener).
*/
protected void initialize () {
super.initialize ();
CookiesChangeListener listener = new CookiesChangeListener(getClass());
putProperty(PROP_COOKIE_CHANGE, listener);
}
/* Activates cookie change listener. */
protected void addNotify () {
super.addNotify();
CookiesChangeListener listener =
(CookiesChangeListener)getProperty (PROP_COOKIE_CHANGE);
listener.setActive(true);
}
/* Deactivates cookie changes listener */
protected void removeNotify () {
super.removeNotify();
CookiesChangeListener listener =
(CookiesChangeListener)getProperty (PROP_COOKIE_CHANGE);
listener.setActive(false);
}
/** Getter for cookies.
* @return the set of cookies for this
*/
private Class[] getCookies () {
Class[] ret = (Class[])getProperty (PROP_COOKIES);
if (ret != null) return ret;
ret = cookieClasses ();
putProperty (PROP_COOKIES, ret);
return ret;
}
/** Test for enablement based on the cookies of selected nodes.
* Generally subclasses should not override this except for strange
* purposes, and then only calling the super method and adding a check.
* Just use {@link #cookieClasses} and {@link #mode} to specify
* the enablement logic.
* @param activatedNodes the set of activated nodes
* @return <code>true</code> to enable
*/
protected boolean enable (Node[] activatedNodes) {
if (activatedNodes.length == 0)
return false;
// sets new nodes to cookie change listener
((CookiesChangeListener)getProperty(PROP_COOKIE_CHANGE)).
setNodes(activatedNodes);
// perform enable / disable logic
return doEnable(activatedNodes);
}
/** Helper, actually performs enable / disable logic */
boolean doEnable (Node[] activatedNodes) {
int supported = resolveSupported(activatedNodes);
if (supported == 0)
return false;
int mode = mode ();
return
// [PENDING] shouldn't MODE_ONE also say: && supported == 1? --jglick
((mode & MODE_ONE) != 0) ||
(((mode & MODE_ALL) != 0) && (supported == activatedNodes.length)) ||
(((mode & MODE_EXACTLY_ONE) != 0) && (activatedNodes.length == 1)) ||
(((mode & MODE_SOME) != 0) && (supported < activatedNodes.length));
}
/**
* Implementation of the above method.
*
* @param activatedNodes gives array of actually activated nodes.
* @return number of supported classes
*/
private int resolveSupported (Node[] activatedNodes) {
int total = activatedNodes.length;
int ret = 0;
Class[] cookies = getCookies();
for (int i = 0; i < total; i++) {
for (int j = 0; j < cookies.length; j++) {
// test for supported cookies
if (activatedNodes[i].getCookie(cookies[j]) != null) {
ret++;
break;
}
}
}
return ret;
}
/** Tracks changes of cookie classes in currently selected nodes
*/
static final class CookiesChangeListener extends NodeAdapter {
/** The nodes we are currently listening */
Node[] nodes;
/** True if we are active */
boolean active;
/** the class of the action we work with */
private Class clazz;
/** Constructor - asociates with given cookie action class
*/
public CookiesChangeListener (Class clazz) {
this.clazz = clazz;
}
/** Activates/deactivates the listener.
* Listener tracks cookie classes changes only if it is active.
* @param active active flag
*/
void setActive (boolean active) {
if (this.active == active)
return;
this.active = active;
if (nodes != null) {
if (active)
attachListeners(nodes);
else {
detachListeners(nodes);
nodes = null;
}
}
}
/** Is the listener active?
*/
boolean isActive () {
return active;
}
/** Sets the nodes to work on */
void setNodes (Node[] newNodes) {
// detach old nodes
if (nodes != null)
detachListeners(nodes);
// attach to new nodes if we are active
if ((newNodes != null) && active) {
attachListeners(newNodes);
}
nodes = newNodes;
}
/** Removes itself as a listener from given nodes */
void detachListeners (Node[] nodes) {
for (int i = 0; i < nodes.length; i++) {
nodes[i].removeNodeListener(this);
}
}
/** Attach itself as a listener to the given nodes */
void attachListeners (Node[] nodes) {
for (int i = 0; i < nodes.length; i++) {
nodes[i].addNodeListener(this);
}
}
/** Reacts to the cookie classes change -
* calls enable on asociated action */
public void propertyChange (PropertyChangeEvent ev) {
// filter only cookie classes changes
if (!Node.PROP_COOKIE.equals(ev.getPropertyName()))
return;
// find asociated action
CookieAction a = (CookieAction)findObject(clazz);
// let the action to enable / disable itself
if ((nodes != null) && (a != null)) {
a.setEnabled (a.enable (nodes));
}
}
} // end of CookiesChangeListener
}
/*
* Log
* 11 Gandalf 1.10 1/12/00 Pavel Buzek I18N
* 10 Gandalf 1.9 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 9 Gandalf 1.8 10/6/99 Petr Jiricka Clarified Javadoc for
* cookieClasses
* 8 Gandalf 1.7 8/9/99 Ian Formanek Generated Serial Version
* UID
* 7 Gandalf 1.6 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 6 Gandalf 1.5 6/1/99 Jesse Glick [JavaDoc]
* 5 Gandalf 1.4 4/8/99 David Simonek debigging comments
* removed...
* 4 Gandalf 1.3 3/29/99 David Simonek cookie action now
* listens on cookie changes
* 3 Gandalf 1.2 3/26/99 Jesse Glick [JavaDoc]
* 2 Gandalf 1.1 3/26/99 Jesse Glick [JavaDoc]
* 1 Gandalf 1.0 1/5/99 Ian Formanek
* $
* Beta Change History:
* 0 Tuborg 0.12 --/--/98 Jan Formanek reflecting changes in cookies, bugfix
*/